昨天我們聊到繼承的鐵則:一個 Class 只能有一個老爸(單一繼承)。老爸寫好的程式碼,小孩可以直接拿來用。
但現實開發中,常常會遇到兩種讓人頭痛的情境:
抽象類別(Abstract Class):寫了一半的半成品試卷
在類別前面掛上 abstract,它就正式變成抽象類別。它的靈魂在於:共通的答案老爸先幫你寫好,專屬的申論題留白讓你自由發揮。
Java
public abstract class Animal {
// 1. 選擇題(一般方法):大家呼吸都一樣,老爸直接寫好完整大括號 {}
public void breathe() {
System.out.println("呼吸空氣中...");
}
// 2. 申論題(抽象方法):老爸只出題目立項,後面沒有大括號 {},只有一個分號 ; 結尾
public abstract void makeSound();
}
為什麼抽象類別絕對不能 new?
如果有人想不開寫出 Animal a = new Animal();,編譯器會當場亮紅字阻擋,原因非常簡單:
世上根本沒有一種生物叫「動物」:
現實中你能看到貓、摸到狗,但抓不出一個純粹的動物實體。概念無法直接具象化,它只是個大分類。
空頭支票發不出聲音:
老爸的 makeSound() 後面連個大括號 {} 都沒有,資訊根本不齊全。如果 Java 准許你生出這個物件,當你呼叫它時,電腦根本不知道該播放狗吠還是貓叫,不可能對著空氣猜你的心意。
繼承抽象類別:拿老爸的考卷交出自己的答案
當子類別繼承抽象老爸時,就像在考場領到這份「寫了一半的考卷」:
坐享其成(是非選擇題):
呼吸方式大家完全相同,貓咪直接承襲老爸寫好的 breathe(),連一行程式碼都不必重打。
各憑本事(申論題):
考卷上訂了題目:「請作答你該怎麼叫?」後面的空白處老爸不幫你填,必須用@Override覆寫填上自己的大括號。身為貓咪,你的申論題就填「喵喵喵」;身為小狗,你的申論題就填「汪汪汪」。
每個人依自己的身分交出專屬答案,把整張考卷的空格全部填滿,這張圖紙才算正式完工,也才能真正產生實體!
public class Cat extends Animal {
// 把老爸留白的申論題填滿!
@Override
public void makeSound() {
System.out.println("喵喵喵!");
}
}
把老爸未完工的申論題全數作答完畢,這張藍圖才算真正合格落成,這時你才能放心大喊 new Cat(),把活生生的貓咪實體召喚出來!
2. 介面(Interface):「外掛技能合約包」
在 Java 的世界裡,家規非常嚴格:一個 Class 只能認一個老爸(單一繼承)。
小豬(Pig)已經繼承了動物老爸(Animal),但今天米其林大廚走過來,想賦予小豬第二個身分「可食用食材」,這時小豬不能再去認廚房當乾爹,該怎麼辦?
答案就是:祭出 interface(介面)簽合約!
介面是個甩手掌櫃,它一丁點程式碼都不寫,純粹開出一張「空頭規格清單」:
public interface Edible {
// 介面裡只開規格:誰簽約,誰就必須交代「到底怎麼吃」!
String youMayEat();
}
implements 就是:身分認證簽約,加分題自己答!
小豬想兼差獲得「食材證照」,就在類別後面大方寫上 implements Edible(簽署實作合約):
// 小豬只有一個親生老爸 Animal,但簽下 Edible 食材技能合約!
public class Pig extends Animal implements Edible {
// 1. 履行老爸 Animal 的基本義務(把申論題填完)
@Override
public void makeSound() {
System.out.println("Oink! ");
}
// 2. 履行 Edible 合約的技能承諾(加分題交代如何食用)
@Override
public String youMayEat() {
return "可以做成美味的炸豬排!";
}
}
為什麼介面能讓小豬「身兼多職、海納百川」?
血緣只有一個,身分可以無限多:
小豬只能有一個老爸(extends Animal),但它可以同時簽好幾份合約:
implements Edible, Pet, Sellable
它既是「可食用的(Edible)」、又是「寵物(Pet)」、還是「可買賣的商品(Sellable)」。
你只要敢簽名(implements),就必須在自己的大括號裡,把(抽象方法)一題一題用 @Override 交代清楚,程式才算合格過關!
一句話記住:
extends 決定你是誰的種,implements 決定你身上掛了幾張專業證照!